﻿using Core.Framework.Distributed.Model;
using Core.Framework.Util;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace Core.Framework.Distributed
{
    public class Lock : CoreDistributed, ILock
    {

        string _token { get { return Guid.NewGuid().ToString(); } }


        public async Task<ApplyLockContext> ApplyAsync(ApplyLockEntity entity, Action success, Action fail)
        {

            return await Task.Run(() =>
            {
                ApplyLockContext context = new ApplyLockContext
                {
                    ApplyToken = _token
                };

                this.StructureApplyParameter(entity, () =>
                {
                    context.IsApplySuccess = true;

                    this.ThreadAbort(context.ApplyExpirationThread);

                    success.Invoke();

                    context.IsTaskComplete = true;

                    context.WaitHandle.Set();

                }, () => {

                    context.IsApplySuccess = false;

                    this.ThreadAbort(context.ApplyExpirationThread);

                    fail.Invoke();

                    context.IsTaskComplete = true;

                    context.WaitHandle.Set();

                    }, ref context);


                // 加入任务字典
                _TaskDic.TryAdd(context.ApplyToken, context.TaskEntity);

                // 开始申请
                var state = BeginSend($"Lock:{context.ApplyEntity.TryToJson()}");

                // 验证申请状态
                if (!state)
                {
                    _TaskDic.TryRemove(context.ApplyToken, out TaskDicEntity taskEntity);
                    Thread.Sleep(1000);
                    var ttt = this.ApplyWaitAsync(entity, success);
                    return ttt.Result;
                }
                else
                {
                    // 处理申请超时
                    this.ExecApplyExpirationTime(context);
                }

                return context;


            });

        }


        public async Task<ApplyLockContext> ApplyWaitAsync(ApplyLockEntity entity, Action success)
        {

            return await Task.Run(() =>
            {
                ApplyLockContext context = new ApplyLockContext
                {
                    ApplyToken = _token,
                    IsCancel = false,
                    IsApplySuccess = false,
                    IsTaskComplete = false
                };

                this.StructureApplyParameter(entity, () => {

                    context.IsApplySuccess = true;

                    this.ThreadAbort(context.ApplyExpirationThread);

                    success.Invoke();

                    context.IsTaskComplete = true;

                    context.WaitHandle.Set();

                }, () => {

                    context.IsApplySuccess = false;

                    this.ThreadAbort(context.ApplyExpirationThread);

                    context.WaitHandle.Set();

                }, ref context);

                // 加入任务字典
                _TaskDic.TryAdd(context.ApplyToken, context.TaskEntity);

                // 标记阻塞
                context.ApplyEntity.IsWait = 1;

                // 开始申请
                var state = BeginSend($"Lock:{context.ApplyEntity.TryToJson()}");

                // 验证申请状态
                if (!state)
                {
                    _TaskDic.TryRemove(context.ApplyToken, out TaskDicEntity taskEntity);
                    Thread.Sleep(1000);
                    var ttt = this.ApplyWaitAsync(entity, success);
                    return ttt.Result;
                }
                else
                {
                    // 处理申请超时
                    this.ExecApplyExpirationTime(context);
                }


                return context;


            });
        }




        #region Common

        /// <summary>
        /// 构造申请上下文
        /// </summary>
        /// <param name="entity">申请信息</param>
        /// <param name="success">申请成功触发</param>
        /// <param name="fail">申请失败触发</param>
        /// <param name="context">申请上下文信息</param>
        private void StructureApplyParameter(
            ApplyLockEntity entity,
            Action success,
            Action fail,
            ref ApplyLockContext context
            )
        {
            context.ApplyEntity = new ApplyLockWaitEntity
            {
                MaxCount = entity.MaxCount,
                TaskExpirationTime = entity.TaskExpirationTime,
                Topic = entity.Topic,
                Token = context.ApplyToken
            };

            context.TaskEntity = new TaskDicEntity
            {
                TaskFail = fail,
                TaskSuccess = success,
                TaskExpirationTime = entity.TaskExpirationTime
            };
        }

        /// <summary>
        /// 处理申请超时
        /// </summary>
        /// <param name="context"></param>
        private void ExecApplyExpirationTime(ApplyLockContext context)
        {

            Task.Run(() =>
            {
                // 获取当前线程
                context.ApplyExpirationThread = Thread.CurrentThread;

                // 如果未设置超时事件则默认设置2S
                if (context.ApplyEntity.ApplyExpirationTime == 0)
                    context.ApplyEntity.ApplyExpirationTime = 5000;

                // 线程挂起
                Thread.Sleep(context.ApplyEntity.ApplyExpirationTime);

                if (context.IsApplySuccess != null)
                    return;

                // 标记取消
                context.IsCancel = true;

                // 移除任务
                _TaskDic.TryRemove(context.ApplyToken, out TaskDicEntity taskEntity);

                context.WaitHandle.Set();

            });

            // 线程等待
            context.WaitHandle.WaitOne();

            // 注销超时处理线程
            this.ThreadAbort(context.ApplyExpirationThread);

            // 标记结束时间
            context.EndDate = DateTime.Now;
        }

        /// <summary>
        /// 终止线程
        /// </summary>
        /// <param name="thread"></param>
        private void ThreadAbort(Thread thread)
        {
            try
            {
                if (thread != null)
                    thread.Abort();
            }
            catch
            {
            }
            finally {
                thread = null;
            }
        }

        #endregion
    }
}
